/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.corext.refactoring.rename; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.resources.IFile; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.GroupCategorySet; import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.RenameArguments; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.refactoring.CompilationUnitChange; import org.eclipse.jdt.core.refactoring.IJavaRefactorings; import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; import org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory; import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil; import org.eclipse.jdt.internal.corext.refactoring.Checks; import org.eclipse.jdt.internal.corext.refactoring.CollectingSearchRequestor; import org.eclipse.jdt.internal.corext.refactoring.CuCollectingSearchRequestor; import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester; import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory; import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine; import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup; import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.jdt.internal.corext.refactoring.base.ReferencesInBinaryContext; import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility; import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateCreator; import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateFieldCreator; import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateMethodCreator; import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors; import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil; import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; import org.eclipse.jdt.internal.corext.refactoring.tagging.IDelegateUpdating; import org.eclipse.jdt.internal.corext.refactoring.tagging.IReferenceUpdating; import org.eclipse.jdt.internal.corext.refactoring.tagging.ITextUpdating; import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil; import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; import org.eclipse.jdt.internal.corext.util.JavaModelUtil; import org.eclipse.jdt.internal.corext.util.JdtFlags; import org.eclipse.jdt.internal.corext.util.Messages; import org.eclipse.jdt.internal.corext.util.SearchUtils; import org.eclipse.jdt.ui.JavaElementLabels; import org.eclipse.jdt.ui.refactoring.IRefactoringProcessorIds; import org.eclipse.jdt.ui.refactoring.RefactoringSaveHelper; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; /** * * @author Mohsen Vakilian, nchen - Made the class comply to the API of watched processors. * */ public class RenameFieldProcessor extends JavaRenameProcessor implements IReferenceUpdating, ITextUpdating, IDelegateUpdating { protected static final String ATTRIBUTE_TEXTUAL_MATCHES= "textual"; //$NON-NLS-1$ private static final String ATTRIBUTE_RENAME_GETTER= "getter"; //$NON-NLS-1$ private static final String ATTRIBUTE_RENAME_SETTER= "setter"; //$NON-NLS-1$ private static final String ATTRIBUTE_DELEGATE= "delegate"; //$NON-NLS-1$ private static final String ATTRIBUTE_DEPRECATE= "deprecate"; //$NON-NLS-1$ protected IField fField; private SearchResultGroup[] fReferences; private TextChangeManager fChangeManager; protected boolean fUpdateReferences; protected boolean fUpdateTextualMatches; private boolean fRenameGetter; private boolean fRenameSetter; private boolean fIsComposite; private GroupCategorySet fCategorySet; private boolean fDelegateUpdating; private boolean fDelegateDeprecation; /** * Creates a new rename field processor. * * @param field the field, or <code>null</code> if invoked by scripting */ public RenameFieldProcessor(IField field) { this(field, new TextChangeManager(true), null); fIsComposite= false; } /** * Creates a new rename enum const processor. * * @param arguments the arguments * * @param status the status */ public RenameFieldProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) { this(null); RefactoringStatus initializeStatus= initialize(arguments); status.merge(initializeStatus); } /** * Creates a new rename field processor. * <p> * This constructor is only used by <code>RenameTypeProcessor</code>. * </p> * * @param field the field * @param manager the change manager * @param categorySet the group category set */ RenameFieldProcessor(IField field, TextChangeManager manager, GroupCategorySet categorySet) { initialize(field); fChangeManager= manager; fCategorySet= categorySet; fDelegateUpdating= false; fDelegateDeprecation= true; fIsComposite= true; } private void initialize(IField field) { fField= field; if (fField != null) setNewElementName(fField.getElementName()); fUpdateReferences= true; fUpdateTextualMatches= false; fRenameGetter= false; fRenameSetter= false; } @Override public String getIdentifier() { return IRefactoringProcessorIds.RENAME_FIELD_PROCESSOR; } @Override public boolean isApplicable() throws CoreException { return RefactoringAvailabilityTester.isRenameFieldAvailable(fField); } @Override public String getProcessorName() { return RefactoringCoreMessages.RenameFieldRefactoring_name; } @Override protected String[] getAffectedProjectNatures() throws CoreException { return JavaProcessors.computeAffectedNatures(fField); } public IField getField() { return fField; } @Override public Object[] getElements() { return new Object[] { fField}; } @Override protected RenameModifications computeRenameModifications() throws CoreException { RenameModifications result= new RenameModifications(); result.rename(fField, new RenameArguments(getNewElementName(), getUpdateReferences())); if (fRenameGetter) { IMethod getter= getGetter(); if (getter != null) { result.rename(getter, new RenameArguments(getNewGetterName(), getUpdateReferences())); } } if (fRenameSetter) { IMethod setter= getSetter(); if (setter != null) { result.rename(setter, new RenameArguments(getNewSetterName(), getUpdateReferences())); } } return result; } @Override protected IFile[] getChangedFiles() { return ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); } //---- IRenameProcessor ------------------------------------- public final String getCurrentElementName() { return fField.getElementName(); } public final String getCurrentElementQualifier() { return fField.getDeclaringType().getFullyQualifiedName('.'); } public RefactoringStatus checkNewElementName(String newName) throws CoreException { Assert.isNotNull(newName, "new name"); //$NON-NLS-1$ RefactoringStatus result= Checks.checkFieldName(newName, fField); if (isInstanceField(fField) && (!Checks.startsWithLowerCase(newName))) result.addWarning(fIsComposite ? Messages.format(RefactoringCoreMessages.RenameFieldRefactoring_should_start_lowercase2, new String[] { BasicElementLabels.getJavaElementName(newName), getDeclaringTypeLabel() }) : RefactoringCoreMessages.RenameFieldRefactoring_should_start_lowercase); if (Checks.isAlreadyNamed(fField, newName)) result.addError(fIsComposite ? Messages.format(RefactoringCoreMessages.RenameFieldRefactoring_another_name2, new String[] { BasicElementLabels.getJavaElementName(newName), getDeclaringTypeLabel() }) : RefactoringCoreMessages.RenameFieldRefactoring_another_name, JavaStatusContext.create(fField)); if (fField.getDeclaringType().getField(newName).exists()) result.addError(fIsComposite ? Messages.format(RefactoringCoreMessages.RenameFieldRefactoring_field_already_defined2, new String[] { BasicElementLabels.getJavaElementName(newName), getDeclaringTypeLabel() }) : RefactoringCoreMessages.RenameFieldRefactoring_field_already_defined, JavaStatusContext.create(fField.getDeclaringType().getField(newName))); return result; } private String getDeclaringTypeLabel() { return JavaElementLabels.getElementLabel(fField.getDeclaringType(), JavaElementLabels.ALL_DEFAULT); } public Object getNewElement() { return fField.getDeclaringType().getField(getNewElementName()); } //---- ITextUpdating2 --------------------------------------------- public boolean canEnableTextUpdating() { return true; } public boolean getUpdateTextualMatches() { return fUpdateTextualMatches; } public void setUpdateTextualMatches(boolean update) { fUpdateTextualMatches= update; } //---- IReferenceUpdating ----------------------------------- public void setUpdateReferences(boolean update) { fUpdateReferences= update; } public boolean getUpdateReferences() { return fUpdateReferences; } //-- getter/setter -------------------------------------------------- /** * @return Error message or <code>null</code> if getter can be renamed. * @throws CoreException should not happen */ public String canEnableGetterRenaming() throws CoreException { if (fField.getDeclaringType().isInterface()) return getGetter() == null ? "" : null; //$NON-NLS-1$ IMethod getter= getGetter(); if (getter == null) return ""; //$NON-NLS-1$ final NullProgressMonitor monitor= new NullProgressMonitor(); if (MethodChecks.isVirtual(getter)) { final ITypeHierarchy hierarchy= getter.getDeclaringType().newTypeHierarchy(monitor); if (MethodChecks.isDeclaredInInterface(getter, hierarchy, monitor) != null || MethodChecks.overridesAnotherMethod(getter, hierarchy) != null) return RefactoringCoreMessages.RenameFieldRefactoring_declared_in_supertype; } return null; } /** * @return Error message or <code>null</code> if setter can be renamed. * @throws CoreException should not happen */ public String canEnableSetterRenaming() throws CoreException { if (fField.getDeclaringType().isInterface()) return getSetter() == null ? "" : null; //$NON-NLS-1$ IMethod setter= getSetter(); if (setter == null) return ""; //$NON-NLS-1$ final NullProgressMonitor monitor= new NullProgressMonitor(); if (MethodChecks.isVirtual(setter)) { final ITypeHierarchy hierarchy= setter.getDeclaringType().newTypeHierarchy(monitor); if (MethodChecks.isDeclaredInInterface(setter, hierarchy, monitor) != null || MethodChecks.overridesAnotherMethod(setter, hierarchy) != null) return RefactoringCoreMessages.RenameFieldRefactoring_declared_in_supertype; } return null; } public boolean getRenameGetter() { return fRenameGetter; } public void setRenameGetter(boolean renameGetter) { fRenameGetter= renameGetter; } public boolean getRenameSetter() { return fRenameSetter; } public void setRenameSetter(boolean renameSetter) { fRenameSetter= renameSetter; } public IMethod getGetter() throws CoreException { return GetterSetterUtil.getGetter(fField); } public IMethod getSetter() throws CoreException { return GetterSetterUtil.getSetter(fField); } public String getNewGetterName() throws CoreException { IMethod primaryGetterCandidate= JavaModelUtil.findMethod(GetterSetterUtil.getGetterName(fField, new String[0]), new String[0], false, fField.getDeclaringType()); if (!JavaModelUtil.isBoolean(fField) || (primaryGetterCandidate != null && primaryGetterCandidate.exists())) return GetterSetterUtil.getGetterName(fField.getJavaProject(), getNewElementName(), fField.getFlags(), JavaModelUtil.isBoolean(fField), null); //bug 30906 describes why we need to look for other alternatives here return GetterSetterUtil.getGetterName(fField.getJavaProject(), getNewElementName(), fField.getFlags(), false, null); } public String getNewSetterName() throws CoreException { return GetterSetterUtil.getSetterName(fField.getJavaProject(), getNewElementName(), fField.getFlags(), JavaModelUtil.isBoolean(fField), null); } // ------------------- IDelegateUpdating ---------------------- public boolean canEnableDelegateUpdating() { return (getDelegateCount() > 0); } public boolean getDelegateUpdating() { return fDelegateUpdating; } public void setDelegateUpdating(boolean update) { fDelegateUpdating= update; } public void setDeprecateDelegates(boolean deprecate) { fDelegateDeprecation= deprecate; } public boolean getDeprecateDelegates() { return fDelegateDeprecation; } /** * Returns the maximum number of delegates which can be created for the input elements of this * refactoring. * * @return maximum number of delegates */ public int getDelegateCount() { int count= 0; try { if (RefactoringAvailabilityTester.isDelegateCreationAvailable(getField())) count++; if (fRenameGetter && getGetter() != null) count++; if (fRenameSetter && getSetter() != null) count++; } catch (CoreException e) { // no-op } return count; } @Override public int getSaveMode() { return RefactoringSaveHelper.SAVE_REFACTORING; } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { IField primary= (IField) fField.getPrimaryElement(); if (primary == null || !primary.exists()) { String message= Messages.format(RefactoringCoreMessages.RenameFieldRefactoring_deleted, BasicElementLabels.getFileName(fField.getCompilationUnit())); return RefactoringStatus.createFatalErrorStatus(message); } fField= primary; return Checks.checkIfCuBroken(fField); } @Override protected RefactoringStatus doCheckFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException { try{ pm.beginTask("", 18); //$NON-NLS-1$ pm.setTaskName(RefactoringCoreMessages.RenameFieldRefactoring_checking); RefactoringStatus result= new RefactoringStatus(); result.merge(Checks.checkIfCuBroken(fField)); if (result.hasFatalError()) return result; result.merge(checkNewElementName(getNewElementName())); pm.worked(1); result.merge(checkEnclosingHierarchy()); pm.worked(1); result.merge(checkNestedHierarchy(fField.getDeclaringType())); pm.worked(1); if (fUpdateReferences) { pm.setTaskName(RefactoringCoreMessages.RenameFieldRefactoring_searching); fReferences= getReferences(new SubProgressMonitor(pm, 3), result); pm.setTaskName(RefactoringCoreMessages.RenameFieldRefactoring_checking); } else { fReferences= new SearchResultGroup[0]; pm.worked(3); } if (fUpdateReferences) result.merge(analyzeAffectedCompilationUnits()); else Checks.checkCompileErrorsInAffectedFile(result, fField.getResource()); if (getGetter() != null && fRenameGetter) { result.merge(checkAccessor(new SubProgressMonitor(pm, 1), getGetter(), getNewGetterName())); result.merge(Checks.checkIfConstructorName(getGetter(), getNewGetterName(), fField.getDeclaringType().getElementName())); } else { pm.worked(1); } if (getSetter() != null && fRenameSetter) { result.merge(checkAccessor(new SubProgressMonitor(pm, 1), getSetter(), getNewSetterName())); result.merge(Checks.checkIfConstructorName(getSetter(), getNewSetterName(), fField.getDeclaringType().getElementName())); } else { pm.worked(1); } result.merge(createChanges(new SubProgressMonitor(pm, 10))); if (result.hasFatalError()) return result; return result; } finally { pm.done(); } } //---------- private RefactoringStatus checkAccessor(IProgressMonitor pm, IMethod existingAccessor, String newAccessorName) throws CoreException { RefactoringStatus result= new RefactoringStatus(); result.merge(checkAccessorDeclarations(pm, existingAccessor)); result.merge(checkNewAccessor(existingAccessor, newAccessorName)); return result; } private RefactoringStatus checkNewAccessor(IMethod existingAccessor, String newAccessorName) throws CoreException { RefactoringStatus result= new RefactoringStatus(); IMethod accessor= JavaModelUtil.findMethod(newAccessorName, existingAccessor.getParameterTypes(), false, fField.getDeclaringType()); if (accessor == null || !accessor.exists()) return null; String message= Messages.format(RefactoringCoreMessages.RenameFieldRefactoring_already_exists, new String[] { JavaElementUtil.createMethodSignature(accessor), BasicElementLabels.getJavaElementName(fField.getDeclaringType().getFullyQualifiedName('.')) }); result.addError(message, JavaStatusContext.create(accessor)); return result; } private RefactoringStatus checkAccessorDeclarations(IProgressMonitor pm, IMethod existingAccessor) throws CoreException { RefactoringStatus result= new RefactoringStatus(); SearchPattern pattern= SearchPattern.createPattern(existingAccessor, IJavaSearchConstants.DECLARATIONS, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); IJavaSearchScope scope= SearchEngine.createHierarchyScope(fField.getDeclaringType()); SearchResultGroup[] groupDeclarations= RefactoringSearchEngine.search(pattern, scope, pm, result); Assert.isTrue(groupDeclarations.length > 0); if (groupDeclarations.length != 1) { String message= Messages.format(RefactoringCoreMessages.RenameFieldRefactoring_overridden, JavaElementUtil.createMethodSignature(existingAccessor)); result.addError(message); } else { SearchResultGroup group= groupDeclarations[0]; Assert.isTrue(group.getSearchResults().length > 0); if (group.getSearchResults().length != 1) { String message= Messages.format(RefactoringCoreMessages.RenameFieldRefactoring_overridden_or_overrides, JavaElementUtil.createMethodSignature(existingAccessor)); result.addError(message); } } return result; } private static boolean isInstanceField(IField field) throws CoreException { if (JavaModelUtil.isInterfaceOrAnnotation(field.getDeclaringType())) return false; else return !JdtFlags.isStatic(field); } private RefactoringStatus checkNestedHierarchy(IType type) throws CoreException { IType[] nestedTypes= type.getTypes(); if (nestedTypes == null) return null; RefactoringStatus result= new RefactoringStatus(); for (int i= 0; i < nestedTypes.length; i++) { IField otherField= nestedTypes[i].getField(getNewElementName()); if (otherField.exists()) { String msg= Messages.format( RefactoringCoreMessages.RenameFieldRefactoring_hiding, new String[] { BasicElementLabels.getJavaElementName(fField.getElementName()), BasicElementLabels.getJavaElementName(getNewElementName()), BasicElementLabels.getJavaElementName(nestedTypes[i].getFullyQualifiedName('.')) }); result.addWarning(msg, JavaStatusContext.create(otherField)); } result.merge(checkNestedHierarchy(nestedTypes[i])); } return result; } private RefactoringStatus checkEnclosingHierarchy() { IType current= fField.getDeclaringType(); if (Checks.isTopLevel(current)) return null; RefactoringStatus result= new RefactoringStatus(); while (current != null) { IField otherField= current.getField(getNewElementName()); if (otherField.exists()) { String msg= Messages.format( RefactoringCoreMessages.RenameFieldRefactoring_hiding2, new String[] { BasicElementLabels.getJavaElementName(getNewElementName()), BasicElementLabels.getJavaElementName(current.getFullyQualifiedName('.')), BasicElementLabels.getJavaElementName(otherField.getElementName()) }); result.addWarning(msg, JavaStatusContext.create(otherField)); } current= current.getDeclaringType(); } return result; } /* * (non java-doc) * Analyzes all compilation units in which type is referenced */ private RefactoringStatus analyzeAffectedCompilationUnits() throws CoreException { RefactoringStatus result= new RefactoringStatus(); fReferences= Checks.excludeCompilationUnits(fReferences, result); if (result.hasFatalError()) return result; result.merge(Checks.checkCompileErrorsInAffectedFiles(fReferences)); return result; } private SearchPattern createSearchPattern() { return SearchPattern.createPattern(fField, IJavaSearchConstants.REFERENCES); } private IJavaSearchScope createRefactoringScope() throws CoreException { return RefactoringScopeFactory.create(fField, true, false); } private SearchResultGroup[] getReferences(IProgressMonitor pm, RefactoringStatus status) throws CoreException { String binaryRefsDescription= Messages.format(RefactoringCoreMessages.ReferencesInBinaryContext_ref_in_binaries_description, BasicElementLabels.getJavaElementName(getCurrentElementName())); ReferencesInBinaryContext binaryRefs= new ReferencesInBinaryContext(binaryRefsDescription); SearchResultGroup[] result= RefactoringSearchEngine.search(createSearchPattern(), createRefactoringScope(), new CuCollectingSearchRequestor(binaryRefs), pm, status); binaryRefs.addErrorIfNecessary(status); return result; } @Override public Change createChange(IProgressMonitor monitor) throws CoreException { try { monitor.beginTask(RefactoringCoreMessages.RenameFieldRefactoring_checking, 1); TextChange[] changes= fChangeManager.getAllChanges(); return new DynamicValidationRefactoringChange(createRefactoringDescriptor(), getProcessorName(), changes); } finally { monitor.done(); } } /** * Overridden by subclasses. * * @return return the refactoring descriptor for this refactoring */ protected RenameJavaElementDescriptor createRefactoringDescriptor() { String project= null; IJavaProject javaProject= fField.getJavaProject(); if (javaProject != null) project= javaProject.getElementName(); int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE; try { if (!Flags.isPrivate(fField.getFlags())) flags|= RefactoringDescriptor.MULTI_CHANGE; } catch (JavaModelException exception) { JavaPlugin.log(exception); } final IType declaring= fField.getDeclaringType(); try { if (declaring.isAnonymous() || declaring.isLocal()) flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; } catch (JavaModelException exception) { JavaPlugin.log(exception); } final String description= Messages.format(RefactoringCoreMessages.RenameFieldRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(fField.getElementName())); final String header= Messages.format(RefactoringCoreMessages.RenameFieldProcessor_descriptor_description, new String[] { BasicElementLabels.getJavaElementName(fField.getElementName()), JavaElementLabels.getElementLabel(fField.getParent(), JavaElementLabels.ALL_FULLY_QUALIFIED), getNewElementName() }); final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); if (fRenameGetter) comment.addSetting(RefactoringCoreMessages.RenameFieldRefactoring_setting_rename_getter); if (fRenameSetter) comment.addSetting(RefactoringCoreMessages.RenameFieldRefactoring_setting_rename_settter); final RenameJavaElementDescriptor descriptor= RefactoringSignatureDescriptorFactory.createRenameJavaElementDescriptor(IJavaRefactorings.RENAME_FIELD); descriptor.setProject(project); descriptor.setDescription(description); descriptor.setComment(comment.asString()); descriptor.setFlags(flags); descriptor.setJavaElement(fField); descriptor.setNewName(getNewElementName()); descriptor.setUpdateReferences(fUpdateReferences); descriptor.setUpdateTextualOccurrences(fUpdateTextualMatches); descriptor.setRenameGetters(fRenameGetter); descriptor.setRenameSetters(fRenameSetter); descriptor.setKeepOriginal(fDelegateUpdating); descriptor.setDeprecateDelegate(fDelegateDeprecation); return descriptor; } private RefactoringStatus createChanges(IProgressMonitor pm) throws CoreException { pm.beginTask(RefactoringCoreMessages.RenameFieldRefactoring_checking, 10); RefactoringStatus result= new RefactoringStatus(); if (!fIsComposite) fChangeManager.clear(); // Delegate creation requires ASTRewrite which // creates a new change -> do this first. if (fDelegateUpdating) result.merge(addDelegates()); addDeclarationUpdate(); if (fUpdateReferences) { addReferenceUpdates(new SubProgressMonitor(pm, 1)); result.merge(analyzeRenameChanges(new SubProgressMonitor(pm, 2))); if (result.hasFatalError()) return result; } else { pm.worked(3); } if (getGetter() != null && fRenameGetter) { addGetterOccurrences(new SubProgressMonitor(pm, 1), result); } else { pm.worked(1); } if (getSetter() != null && fRenameSetter) { addSetterOccurrences(new SubProgressMonitor(pm, 1), result); } else { pm.worked(1); } if (fUpdateTextualMatches) { addTextMatches(new SubProgressMonitor(pm, 5)); } else { pm.worked(5); } pm.done(); return result; } private void addDeclarationUpdate() throws CoreException { ISourceRange nameRange= fField.getNameRange(); TextEdit textEdit= new ReplaceEdit(nameRange.getOffset(), nameRange.getLength(), getNewElementName()); ICompilationUnit cu= fField.getCompilationUnit(); String groupName= RefactoringCoreMessages.RenameFieldRefactoring_Update_field_declaration; addTextEdit(fChangeManager.get(cu), groupName, textEdit); } private RefactoringStatus addDelegates() throws JavaModelException, CoreException { RefactoringStatus status= new RefactoringStatus(); CompilationUnitRewrite rewrite= new CompilationUnitRewrite(fField.getCompilationUnit()); rewrite.setResolveBindings(true); // add delegate for the field if (RefactoringAvailabilityTester.isDelegateCreationAvailable(fField)) { FieldDeclaration fieldDeclaration= ASTNodeSearchUtil.getFieldDeclarationNode(fField, rewrite.getRoot()); if (fieldDeclaration.fragments().size() > 1) { status.addWarning( Messages.format(RefactoringCoreMessages.DelegateCreator_cannot_create_field_delegate_more_than_one_fragment, BasicElementLabels.getJavaElementName(fField.getElementName())), JavaStatusContext.create(fField)); } else if (((VariableDeclarationFragment)fieldDeclaration.fragments().get(0)).getInitializer() == null) { status.addWarning(Messages.format(RefactoringCoreMessages.DelegateCreator_cannot_create_field_delegate_no_initializer, BasicElementLabels.getJavaElementName(fField.getElementName())), JavaStatusContext.create(fField)); } else { DelegateFieldCreator creator= new DelegateFieldCreator(); creator.setDeclareDeprecated(fDelegateDeprecation); creator.setDeclaration(fieldDeclaration); creator.setNewElementName(getNewElementName()); creator.setSourceRewrite(rewrite); creator.prepareDelegate(); creator.createEdit(); } } // add delegates for getter and setter methods // there may be getters even if the field is static final if (getGetter() != null && fRenameGetter) addMethodDelegate(getGetter(), getNewGetterName(), rewrite); if (getSetter() != null && fRenameSetter) addMethodDelegate(getSetter(), getNewSetterName(), rewrite); final CompilationUnitChange change= rewrite.createChange(true); if (change != null) { change.setKeepPreviewEdits(true); fChangeManager.manage(fField.getCompilationUnit(), change); } return status; } private void addMethodDelegate(IMethod getter, String newName, CompilationUnitRewrite rewrite) throws JavaModelException { MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(getter, rewrite.getRoot()); DelegateCreator creator= new DelegateMethodCreator(); creator.setDeclareDeprecated(fDelegateDeprecation); creator.setDeclaration(declaration); creator.setNewElementName(newName); creator.setSourceRewrite(rewrite); creator.prepareDelegate(); creator.createEdit(); } private void addTextEdit(TextChange change, String groupName, TextEdit textEdit) { if (fIsComposite) TextChangeCompatibility.addTextEdit(change, groupName, textEdit, fCategorySet); else TextChangeCompatibility.addTextEdit(change, groupName, textEdit); } private void addReferenceUpdates(IProgressMonitor pm) { pm.beginTask("", fReferences.length); //$NON-NLS-1$ String editName= RefactoringCoreMessages.RenameFieldRefactoring_Update_field_reference; for (int i= 0; i < fReferences.length; i++) { ICompilationUnit cu= fReferences[i].getCompilationUnit(); if (cu == null) continue; SearchMatch[] results= fReferences[i].getSearchResults(); for (int j= 0; j < results.length; j++) { addTextEdit(fChangeManager.get(cu), editName, createTextChange(results[j])); } pm.worked(1); } } private TextEdit createTextChange(SearchMatch match) { return new ReplaceEdit(match.getOffset(), match.getLength(), getNewElementName()); } private void addGetterOccurrences(IProgressMonitor pm, RefactoringStatus status) throws CoreException { addAccessorOccurrences(pm, getGetter(), RefactoringCoreMessages.RenameFieldRefactoring_Update_getter_occurrence, getNewGetterName(), status); } private void addSetterOccurrences(IProgressMonitor pm, RefactoringStatus status) throws CoreException { addAccessorOccurrences(pm, getSetter(), RefactoringCoreMessages.RenameFieldRefactoring_Update_setter_occurrence, getNewSetterName(), status); } private void addAccessorOccurrences(IProgressMonitor pm, IMethod accessor, String editName, String newAccessorName, RefactoringStatus status) throws CoreException { Assert.isTrue(accessor.exists()); IJavaSearchScope scope= RefactoringScopeFactory.create(accessor); SearchPattern pattern= SearchPattern.createPattern(accessor, IJavaSearchConstants.ALL_OCCURRENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); SearchResultGroup[] groupedResults= RefactoringSearchEngine.search( pattern, scope, new MethodOccurenceCollector(accessor.getElementName()), pm, status); for (int i= 0; i < groupedResults.length; i++) { ICompilationUnit cu= groupedResults[i].getCompilationUnit(); if (cu == null) continue; SearchMatch[] results= groupedResults[i].getSearchResults(); for (int j= 0; j < results.length; j++) { SearchMatch searchResult= results[j]; TextEdit edit= new ReplaceEdit(searchResult.getOffset(), searchResult.getLength(), newAccessorName); addTextEdit(fChangeManager.get(cu), editName, edit); } } } private void addTextMatches(IProgressMonitor pm) throws CoreException { TextMatchUpdater.perform(pm, createRefactoringScope(), this, fChangeManager, fReferences); } //---------------- private RefactoringStatus analyzeRenameChanges(IProgressMonitor pm) throws CoreException { ICompilationUnit[] newWorkingCopies= null; WorkingCopyOwner newWCOwner= new WorkingCopyOwner() { /* must subclass */ }; try { pm.beginTask("", 2); //$NON-NLS-1$ RefactoringStatus result= new RefactoringStatus(); SearchResultGroup[] oldReferences= fReferences; List<ICompilationUnit> compilationUnitsToModify= new ArrayList<ICompilationUnit>(); if (fIsComposite) { // limited change set, no accessors. for (int i= 0; i < oldReferences.length; i++) compilationUnitsToModify.add(oldReferences[i].getCompilationUnit()); compilationUnitsToModify.add(fField.getCompilationUnit()); } else { // include all cus, including accessors compilationUnitsToModify.addAll(Arrays.asList(fChangeManager.getAllCompilationUnits())); } newWorkingCopies= RenameAnalyzeUtil.createNewWorkingCopies(compilationUnitsToModify.toArray(new ICompilationUnit[compilationUnitsToModify.size()]), fChangeManager, newWCOwner, new SubProgressMonitor(pm, 1)); SearchResultGroup[] newReferences= getNewReferences(new SubProgressMonitor(pm, 1), result, newWCOwner, newWorkingCopies); result.merge(RenameAnalyzeUtil.analyzeRenameChanges2(fChangeManager, oldReferences, newReferences, getNewElementName())); return result; } finally{ pm.done(); if (newWorkingCopies != null){ for (int i= 0; i < newWorkingCopies.length; i++) { newWorkingCopies[i].discardWorkingCopy(); } } } } private SearchResultGroup[] getNewReferences(IProgressMonitor pm, RefactoringStatus status, WorkingCopyOwner owner, ICompilationUnit[] newWorkingCopies) throws CoreException { pm.beginTask("", 2); //$NON-NLS-1$ ICompilationUnit declaringCuWorkingCopy= RenameAnalyzeUtil.findWorkingCopyForCu(newWorkingCopies, fField.getCompilationUnit()); if (declaringCuWorkingCopy == null) return new SearchResultGroup[0]; IField field= getFieldInWorkingCopy(declaringCuWorkingCopy, getNewElementName()); if (field == null || !field.exists()) return new SearchResultGroup[0]; CollectingSearchRequestor requestor= null; if (fDelegateUpdating && RefactoringAvailabilityTester.isDelegateCreationAvailable(getField())) { // There will be two new matches inside the delegate (the invocation // and the javadoc) which are OK and must not be reported. final IField oldField= getFieldInWorkingCopy(declaringCuWorkingCopy, getCurrentElementName()); requestor= new CollectingSearchRequestor() { @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { if (!oldField.equals(match.getElement())) super.acceptSearchMatch(match); } }; } else requestor= new CollectingSearchRequestor(); SearchPattern newPattern= SearchPattern.createPattern(field, IJavaSearchConstants.REFERENCES); IJavaSearchScope scope= RefactoringScopeFactory.create(fField, true, true); return RefactoringSearchEngine.search(newPattern, owner, scope, requestor, new SubProgressMonitor(pm, 1), status); } private IField getFieldInWorkingCopy(ICompilationUnit newWorkingCopyOfDeclaringCu, String elementName) { IType type= fField.getDeclaringType(); IType typeWc= (IType)JavaModelUtil.findInCompilationUnit(newWorkingCopyOfDeclaringCu, type); if (typeWc == null) return null; return typeWc.getField(elementName); } private RefactoringStatus initialize(JavaRefactoringArguments extended) { final String handle= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT); if (handle != null) { final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false); if (element == null || !element.exists() || element.getElementType() != IJavaElement.FIELD) return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.RENAME_FIELD); else fField= (IField)element; } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT)); final String name= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME); if (name != null && !"".equals(name)) //$NON-NLS-1$ setNewElementName(name); else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME)); final String references= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES); if (references != null) { fUpdateReferences= Boolean.valueOf(references).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES)); final String matches= extended.getAttribute(ATTRIBUTE_TEXTUAL_MATCHES); if (matches != null) { fUpdateTextualMatches= Boolean.valueOf(matches).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_TEXTUAL_MATCHES)); final String getters= extended.getAttribute(ATTRIBUTE_RENAME_GETTER); if (getters != null) fRenameGetter= Boolean.valueOf(getters).booleanValue(); else fRenameGetter= false; final String setters= extended.getAttribute(ATTRIBUTE_RENAME_SETTER); if (setters != null) fRenameSetter= Boolean.valueOf(setters).booleanValue(); else fRenameSetter= false; final String delegate= extended.getAttribute(ATTRIBUTE_DELEGATE); if (delegate != null) { fDelegateUpdating= Boolean.valueOf(delegate).booleanValue(); } else fDelegateUpdating= false; final String deprecate= extended.getAttribute(ATTRIBUTE_DEPRECATE); if (deprecate != null) { fDelegateDeprecation= Boolean.valueOf(deprecate).booleanValue(); } else fDelegateDeprecation= false; return new RefactoringStatus(); } /** * {@inheritDoc} */ public String getDelegateUpdatingTitle(boolean plural) { if (plural) return RefactoringCoreMessages.DelegateFieldCreator_keep_original_renamed_plural; else return RefactoringCoreMessages.DelegateFieldCreator_keep_original_renamed_singular; } ///////////////// //CODINGSPECTATOR ///////////////// public JavaRefactoringDescriptor getOriginalRefactoringDescriptor() { return createRefactoringDescriptor(); } public String getDescriptorID() { return IJavaRefactorings.RENAME_FIELD; } }